msg_tool\scripts\silky/
disasm.rs

1use crate::ext::io::*;
2use anyhow::Result;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub enum Oper {
6    /// Byte
7    B,
8    /// Integer
9    I,
10    /// Address
11    A,
12    /// String
13    S,
14    /// Text
15    T,
16}
17
18use Oper::*;
19
20pub struct Opcodes {
21    pub r#yield: u8,
22    pub add: u8,
23    pub escape_sequence: u8,
24    pub message1: u8,
25    pub message2: u8,
26    pub push_int: u8,
27    pub push_string: u8,
28    pub syscall: u8,
29    pub line_number: u8,
30    pub nop1: u8,
31    pub nop2: u8,
32    pub is_message1_obfuscated: bool,
33}
34
35pub struct Syscalls {
36    pub exec: i32,
37    pub exec_set_character_name: i32,
38}
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq)]
41pub enum SlikyStringType {
42    Internal,
43    Message,
44    Name,
45}
46
47#[derive(Debug, Clone)]
48pub struct SlikyString {
49    pub start: u64,
50    pub len: u64,
51    pub typ: SlikyStringType,
52}
53
54#[derive(Debug, Clone)]
55pub enum Obj {
56    Byte(u8),
57    Int(i32),
58    Str(SlikyString),
59}
60
61pub trait Disasm: std::fmt::Debug {
62    fn stream(&self) -> &MemReader;
63    fn stream_mut(&mut self) -> &mut MemReader;
64    fn opcodes(&self) -> &'static Opcodes;
65    fn operands(&self) -> &'static [(u8, &'static [Oper])];
66    fn syscalls(&self) -> &'static [Syscalls];
67    fn code_offset(&self) -> u32;
68    fn big_endian_addresses(&self) -> &[u32];
69    fn push_big_endian_addresses(&mut self, addr: u32);
70    fn little_endian_addresses(&self) -> &[u32];
71    fn read_header(&mut self) -> Result<()>;
72    fn read_instruction(&mut self) -> Result<(u8, Vec<Obj>)> {
73        let opcode = self.stream_mut().read_u8()?;
74        let mut operands = Vec::new();
75        if let Some((_, ops)) = self.operands().iter().find(|(op, _)| *op == opcode) {
76            for &oper in *ops {
77                operands.push(self.read_operand(oper)?);
78            }
79        }
80        Ok((opcode, operands))
81    }
82    fn read_operand(&mut self, oper: Oper) -> Result<Obj> {
83        match oper {
84            B => Ok(Obj::Byte(self.stream_mut().read_u8()?)),
85            I => Ok(Obj::Int(self.stream_mut().read_i32_be()?)),
86            A => {
87                self.push_big_endian_addresses(self.stream().pos as u32);
88                Ok(Obj::Int(self.stream_mut().read_i32_be()?))
89            }
90            S | T => {
91                let start = self.stream().pos as u64;
92                let s = self.stream_mut().read_cstring()?;
93                Ok(Obj::Str(SlikyString {
94                    start,
95                    len: s.as_bytes_with_nul().len() as u64,
96                    typ: SlikyStringType::Internal,
97                }))
98            }
99        }
100    }
101    fn read_code(&mut self) -> Result<Vec<SlikyString>> {
102        let mut stack: Vec<Obj> = Vec::new();
103        let mut message_start_offset = None;
104        let mut in_ruby = false;
105        let mut texts = Vec::new();
106        self.stream_mut().pos = self.code_offset() as usize;
107        while !self.stream().is_eof() {
108            let instr_offset = self.stream().pos as u64;
109            let (opcode, operands) = self.read_instruction()?;
110            // message instr
111            let opcodes = self.opcodes();
112            if opcode == opcodes.message1 || opcode == opcodes.message2 {
113                if message_start_offset.is_none() {
114                    message_start_offset = Some(instr_offset);
115                }
116            } else if opcode == opcodes.escape_sequence {
117                if let Some(Obj::Byte(b)) = operands.get(0) {
118                    if *b == 0x1 {
119                        in_ruby = true;
120                    }
121                }
122            } else if opcode == opcodes.r#yield && in_ruby {
123                in_ruby = false;
124            } else if opcode == opcodes.push_int
125                && self.stream().cpeek_u8_at(instr_offset + 5)? == opcodes.line_number
126            {
127                // Skip
128            } else if opcode == opcodes.line_number
129                || opcode == opcodes.nop1
130                || opcode == opcodes.nop2
131            {
132                // Skip
133            } else {
134                if let Some(start) = message_start_offset {
135                    let start = start as u64;
136                    let text = SlikyString {
137                        start,
138                        len: instr_offset - start,
139                        typ: SlikyStringType::Message,
140                    };
141                    texts.push(text);
142                }
143                message_start_offset = None;
144                in_ruby = false;
145            }
146            // name instr
147            if opcode == opcodes.push_int || opcode == opcodes.push_string {
148                if !operands.is_empty() {
149                    stack.push(operands[0].clone());
150                }
151            } else if opcode == opcodes.add && stack.len() >= 2 {
152                let value1 = stack.pop().unwrap();
153                let value2 = stack.pop().unwrap();
154                if let (Obj::Int(i1), Obj::Int(i2)) = (value1, value2) {
155                    stack.push(Obj::Int(i1 + i2));
156                }
157            } else if opcode == opcodes.syscall && stack.len() >= 3 {
158                let func_id = stack.pop().unwrap();
159                let exec_id = stack.pop().unwrap();
160                let name = stack.pop().unwrap();
161                if let (Obj::Int(func_id), Obj::Int(exec_id), Obj::Str(name)) =
162                    (func_id, exec_id, name)
163                {
164                    for syscall in self.syscalls() {
165                        if func_id == syscall.exec && exec_id == syscall.exec_set_character_name {
166                            texts.push(SlikyString {
167                                start: name.start - 1,
168                                len: name.len + 1,
169                                typ: SlikyStringType::Name,
170                            });
171                        }
172                    }
173                }
174                stack.clear();
175            } else {
176                stack.clear();
177            }
178        }
179        Ok(texts)
180    }
181}
182
183pub const PLUS_OPCODES: Opcodes = Opcodes {
184    r#yield: 0x00,
185    add: 0x34,
186    escape_sequence: 0x1c,
187    message1: 0x0a,
188    message2: 0x0b,
189    push_int: 0x32,
190    push_string: 0x33,
191    syscall: 0x18,
192    line_number: 0xff,
193    nop1: 0xfc,
194    nop2: 0xfd,
195    is_message1_obfuscated: true,
196};
197
198const PLUS_OPERANDS: [(u8, &[Oper]); 53] = [
199    (0x00, &[]),  // yield
200    (0x01, &[]),  // ret
201    (0x02, &[]),  // ldglob1.i8
202    (0x03, &[]),  // ldglob2.i16
203    (0x04, &[]),  // ldglob3.var
204    (0x05, &[]),  // ldglob4.var
205    (0x06, &[]),  // ldloc.var
206    (0x07, &[]),  // ldglob5.i8
207    (0x08, &[]),  // ldglob5.i16
208    (0x09, &[]),  // ldglob5.i32
209    (0x0A, &[S]), // message
210    (0x0B, &[T]), // message
211    (0x0C, &[]),  // stglob1.i8
212    (0x0D, &[]),  // stglob2.i16
213    (0x0E, &[]),  // stglob3.var
214    (0x0F, &[]),  // stglob4.var
215    (0x10, &[]),  // stloc.var
216    (0x11, &[]),  // stglob5.i8
217    (0x12, &[]),  // stglob5.i16
218    (0x13, &[]),  // stglob5.i32
219    (0x14, &[A]), // jz
220    (0x15, &[A]), // jmp
221    (0x16, &[A]), // libreg
222    (0x17, &[]),  // libcall
223    (0x18, &[]),  // syscall
224    (0x19, &[I]), // msgid
225    (0x1A, &[I]), // msgid2
226    (0x1B, &[A]), // choice
227    (0x1C, &[B]), // escape sequence
228    (0x32, &[I]), // ldc.i4
229    (0x33, &[S]), // ldstr
230    (0x34, &[]),  // add
231    (0x35, &[]),  // sub
232    (0x36, &[]),  // mul
233    (0x37, &[]),  // div
234    (0x38, &[]),  // mod
235    (0x39, &[]),  // rand
236    (0x3A, &[]),  // logand
237    (0x3B, &[]),  // logor
238    (0x3C, &[]),  // binand
239    (0x3D, &[]),  // binor
240    (0x3E, &[]),  // lt
241    (0x3F, &[]),  // gt
242    (0x40, &[]),  // le
243    (0x41, &[]),  // ge
244    (0x42, &[]),  // eq
245    (0x43, &[]),  // neq
246    (0xFA, &[]),
247    (0xFB, &[]),
248    (0xFC, &[]),
249    (0xFD, &[]),
250    (0xFE, &[]),
251    (0xFF, &[]),
252];
253
254const PLUS_SYSCALLS: [Syscalls; 2] = [
255    Syscalls {
256        exec: 29,
257        exec_set_character_name: 11,
258    },
259    Syscalls {
260        exec: 29,
261        exec_set_character_name: 15,
262    },
263];
264
265#[derive(Debug)]
266pub struct PlusDisasm {
267    stream: MemReader,
268    num_messages: u32,
269    num_special_messages: u32,
270    code_offset: u32,
271    big_endian_addresses: Vec<u32>,
272    little_endian_addresses: Vec<u32>,
273}
274
275impl PlusDisasm {
276    pub fn new(mut stream: MemReader) -> Result<Self> {
277        let num_messages = stream.read_u32()?;
278        let num_special_messages = stream.read_u32()?;
279        let code_offset = 8 + (num_messages + num_special_messages) * 4;
280        Ok(Self {
281            stream,
282            num_messages,
283            num_special_messages,
284            code_offset,
285            big_endian_addresses: Vec::new(),
286            little_endian_addresses: Vec::new(),
287        })
288    }
289}
290
291impl Disasm for PlusDisasm {
292    fn stream(&self) -> &MemReader {
293        &self.stream
294    }
295    fn stream_mut(&mut self) -> &mut MemReader {
296        &mut self.stream
297    }
298    fn opcodes(&self) -> &'static Opcodes {
299        &PLUS_OPCODES
300    }
301    fn operands(&self) -> &'static [(u8, &'static [Oper])] {
302        &PLUS_OPERANDS
303    }
304    fn syscalls(&self) -> &'static [Syscalls] {
305        &PLUS_SYSCALLS
306    }
307    fn code_offset(&self) -> u32 {
308        self.code_offset
309    }
310    fn big_endian_addresses(&self) -> &[u32] {
311        &self.big_endian_addresses
312    }
313    fn push_big_endian_addresses(&mut self, addr: u32) {
314        self.big_endian_addresses.push(addr);
315    }
316    fn little_endian_addresses(&self) -> &[u32] {
317        &self.little_endian_addresses
318    }
319    fn read_header(&mut self) -> Result<()> {
320        for i in 0..self.num_messages + self.num_special_messages {
321            self.little_endian_addresses.push(8 + i * 4);
322        }
323        self.stream.pos = self.code_offset as usize;
324        Ok(())
325    }
326}
327
328const AI6_WIN_OPCODES: Opcodes = Opcodes {
329    r#yield: 0x00,
330    add: 0x34,
331    escape_sequence: 0x1b,
332    message1: 0x0a,
333    message2: 0x0b,
334    push_int: 0x32,
335    push_string: 0x33,
336    syscall: 0x18,
337    line_number: 0xff,
338    nop1: 0xfc,
339    nop2: 0xfd,
340    is_message1_obfuscated: false,
341};
342
343const AI6_WIN_OPERANDS: [(u8, &[Oper]); 48] = [
344    (0x00, &[]),  // yield
345    (0x01, &[]),  // ret
346    (0x02, &[]),  // ldglob1.i8
347    (0x03, &[]),  // ldglob2.i16
348    (0x04, &[]),  // ldglob3.var
349    (0x05, &[]),  // ldglob4.var
350    (0x06, &[]),  // ldloc.var
351    (0x07, &[]),  // ldglob5.i8
352    (0x08, &[]),  // ldglob5.i16
353    (0x09, &[]),  // ldglob5.i32
354    (0x0A, &[S]), // message
355    (0x0B, &[S]), // message
356    (0x0C, &[]),  // stglob1.i8
357    (0x0D, &[]),  // stglob2.i16
358    (0x0E, &[]),  // stglob3.var
359    (0x0F, &[]),  // stglob4.var
360    (0x10, &[]),  // stloc.var
361    (0x11, &[]),  // stglob5.i8
362    (0x12, &[]),  // stglob5.i16
363    (0x13, &[]),  // stglob5.i32
364    (0x14, &[A]), // jz
365    (0x15, &[A]), // jmp
366    (0x16, &[A]), // libreg
367    (0x17, &[]),  // libcall
368    (0x18, &[]),  // syscall
369    (0x19, &[I]), // msgid
370    (0x1A, &[A]), // choice
371    (0x1B, &[B]), // escape sequence
372    (0x32, &[I]), // ldc.i4
373    (0x33, &[S]), // ldstr
374    (0x34, &[]),  // add
375    (0x35, &[]),  // sub
376    (0x36, &[]),  // mul
377    (0x37, &[]),  // div
378    (0x38, &[]),  // mod
379    (0x39, &[]),  // rand
380    (0x3A, &[]),  // logand
381    (0x3B, &[]),  // logor
382    (0x3C, &[]),  // binand
383    (0x3D, &[]),  // binor
384    (0x3E, &[]),  // lt
385    (0x3F, &[]),  // gt
386    (0x40, &[]),  // le
387    (0x41, &[]),  // ge
388    (0x42, &[]),  // eq
389    (0x43, &[]),  // neq
390    (0xFE, &[]),
391    (0xFF, &[]),
392];
393
394const AI6_WIN_SYSCALLS: [Syscalls; 1] = [Syscalls {
395    exec: 31,
396    exec_set_character_name: 15,
397}];
398
399#[derive(Debug)]
400pub struct Ai6WinDisasm {
401    stream: MemReader,
402    num_messages: u32,
403    code_offset: u32,
404    big_endian_addresses: Vec<u32>,
405    little_endian_addresses: Vec<u32>,
406}
407
408impl Ai6WinDisasm {
409    pub fn new(mut stream: MemReader) -> Result<Self> {
410        let num_messages = stream.read_u32()?;
411        let code_offset = 4 + num_messages * 4;
412        Ok(Self {
413            stream,
414            num_messages,
415            code_offset,
416            big_endian_addresses: Vec::new(),
417            little_endian_addresses: Vec::new(),
418        })
419    }
420}
421
422impl Disasm for Ai6WinDisasm {
423    fn stream(&self) -> &MemReader {
424        &self.stream
425    }
426    fn stream_mut(&mut self) -> &mut MemReader {
427        &mut self.stream
428    }
429    fn opcodes(&self) -> &'static Opcodes {
430        &AI6_WIN_OPCODES
431    }
432    fn operands(&self) -> &'static [(u8, &'static [Oper])] {
433        &AI6_WIN_OPERANDS
434    }
435    fn syscalls(&self) -> &'static [Syscalls] {
436        &AI6_WIN_SYSCALLS
437    }
438    fn code_offset(&self) -> u32 {
439        self.code_offset
440    }
441    fn big_endian_addresses(&self) -> &[u32] {
442        &self.big_endian_addresses
443    }
444    fn push_big_endian_addresses(&mut self, addr: u32) {
445        self.big_endian_addresses.push(addr);
446    }
447    fn little_endian_addresses(&self) -> &[u32] {
448        &self.little_endian_addresses
449    }
450    fn read_header(&mut self) -> Result<()> {
451        for i in 0..self.num_messages {
452            self.little_endian_addresses.push(4 + i * 4);
453        }
454        self.stream.pos = self.code_offset as usize;
455        Ok(())
456    }
457}